package com.sample.service.impl;

import java.security.NoSuchAlgorithmException;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.NonUniqueResultException;

import org.springframework.beans.factory.annotation.Autowired;

import com.sample.dao.UserDAO;
import com.sample.model.User;
import com.sample.security.PasswordEncryptor;
import com.sample.service.UserService;
import com.trg.dao.dao.original.GeneralDAO;
import com.trg.search.flex.FlexSearch;
import com.trg.search.flex.FlexSearchWrapper;
import com.trg.search.Search;
import com.trg.search.SearchResult;

public class UserServiceImpl implements UserService {
	@Autowired
	private UserDAO dao;
	
	public void setDao(UserDAO dao) {
		this.dao = dao;
	}
	
	@Resource
	private GeneralDAO generalDAO;
	
	public void setGeneralDao(GeneralDAO generalDAO) {
		this.generalDAO = generalDAO;
	}
	
	@Autowired
	private PasswordEncryptor encryptor;
	
	public void setEncryptor(PasswordEncryptor encryptor) {
		this.encryptor = encryptor;
	}
	
	public User fetchByLoginAndPassword(String login, String password) {
		if (login == null || password == null) return null;
		
		try {
			Search s = new Search();
			s.addFilterEqual("login", login);
			s.addFilterEqual("password", encryptor.encryptPassword(password));
			List<User> list = dao.search(s);
			
			if (list.size() == 1) {
				list.get(0).getRoles(); //fetch roles before losing Hibernate session
				return list.get(0);
			} else {
				return null;
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public User fetchUserByLogin(String login) {
		Search s = new Search();
		s.addFilterEqual("login", login);
		List<User> list = dao.search(s);
		if (list.size() == 1) {
			list.get(0).getRoles(); //fetch roles before losing Hibernate session
			return list.get(0);
		} else {
			return null;
		}
	}
	
	public User addUser(User user, String password) {
		try {
			user.setPassword(encryptor.encryptPassword(password));
			if (user.getBillRate().isNaN()) {
				user.setBillRate(null);
			}
			dao.create(user);
			return user;
		} catch (NoSuchAlgorithmException ex) {
			ex.printStackTrace();
			throw new RuntimeException("Configuration Error. See error log for details.");
		}
	}

	public void changePassword(Long userId, String newPassword) {
		try {
			dao.fetch(userId).setPassword(encryptor.encryptPassword(newPassword));
		} catch (NoSuchAlgorithmException ex) {
			ex.printStackTrace();
			throw new RuntimeException("Configuration Error. See error log for details.");
		}
	}
	
	
	public boolean deleteById(Long id) {
		return dao.deleteById(id);
	}

	public boolean deleteEntity(User user) {
		return dao.deleteEntity(user);
	}

	public User fetch(Long id) {
		User user = dao.fetch(id);
		//fetch lazy roles and projects from Hibernate
		if (user != null) {
			user.getRoles();
			user.getProjects();
		}
		return user;
	}

	public List<User> fetchAll() {
		List<User> users = dao.fetchAll();
		
		for (User user : users) {
			user.setRoles(null);
			user.setProjects(null);
		}
		return users;
	}

	@SuppressWarnings("unchecked")
	public void update(User user) {
		if (user.getBillRate().isNaN()) {
			user.setBillRate(null);
		}
		
		Search s = new Search(User.class);
		s.addFilterEqual("id", user.getId());
		s.addField("password");
		
		List<String> results = generalDAO.search(s);
		if (results.size() != 1) {
			throw new NonUniqueResultException("There should be exactly one user id " + user.getId());
		} else {
			user.setPassword(results.get(0));
		}
		
		dao.update(user);
	}

	public List<User> search(FlexSearch options) {
		return dao.search(new FlexSearchWrapper(options));
	}
	
	public int count(FlexSearch options) {
		return dao.count(new FlexSearchWrapper(options));
	}

	public SearchResult<User> searchAndCount(FlexSearch options) {
		return dao.searchAndCount(new FlexSearchWrapper(options));
	}

	public boolean isConnected(Object object) {
		return dao.isConnected(object);
	}
	
	public void flush() {
		dao.flush();
	}
}
